# -*- coding: utf-8 -*-

# Copyright (c) Huoty, All rights reserved
# Author: Huoty <sudohuoty@163.com>
# CreateTime: 2016-11-04 13:07:57

import os
import sys
import time
import logging
import datetime
from importlib import import_module
from logging.handlers import SMTPHandler as _SMTPHandler


defualt_logger = logging.getLogger()
info = defualt_logger.info
warn = warning = defualt_logger.warning
debug = defualt_logger.debug
error = defualt_logger.error
exception = defualt_logger.exception


DEFAULT_LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"

DEFAULT_ERROR_MAIL_FORMAT = """\
Logger Name:        %(name)s
Message type:       %(levelname)s
Location:           %(pathname)s:%(lineno)d
Module:             %(module)s
Function:           %(funcName)s
Host:               %(host)s
User:               %(user)s
Time:               %(asctime)s

Message:

%(message)s
"""


class SystemLogFormatter(logging.Formatter):
    """支持微秒的日志格式器"""

    converter = datetime.datetime.fromtimestamp

    def formatTime(self, record, datefmt=None):
        ct = self.converter(record.created)
        if datefmt:
            s = ct.strftime(datefmt)
        else:
            t = ct.strftime("%Y-%m-%d %H:%M:%S")
            s = "%s,%03d" % (t, record.msecs)
        return s


class ColoredStreamHandler(logging.StreamHandler):
    """带色彩的流日志处理器"""

    C_BLACK = '\033[0;30m'
    C_RED = '\033[0;31m'
    C_GREEN = '\033[0;32m'
    C_BROWN = '\033[0;33m'
    C_BLUE = '\033[0;34m'
    C_PURPLE = '\033[0;35m'
    C_CYAN = '\033[0;36m'
    C_GREY = '\033[0;37m'

    C_DARK_GREY = '\033[1;30m'
    C_LIGHT_RED = '\033[1;31m'
    C_LIGHT_GREEN = '\033[1;32m'
    C_YELLOW = '\033[1;33m'
    C_LIGHT_BLUE = '\033[1;34m'
    C_LIGHT_PURPLE = '\033[1;35m'
    C_LIGHT_CYAN = '\033[1;36m'
    C_WHITE = '\033[1;37m'

    C_RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.C_DARK_GREY,
                        logging.INFO: self.C_RESET,
                        logging.WARNING: self.C_BROWN,
                        logging.ERROR: self.C_RED,
                        logging.CRITICAL: self.C_LIGHT_RED}
        super(ColoredStreamHandler, self).__init__(*args, **kwargs)

    @property
    def is_tty(self):
        isatty = getattr(self.stream, 'isatty', None)
        return isatty and isatty()

    def emit(self, record):
        try:
            message = self.format(record)
            stream = self.stream
            if not self.is_tty:
                stream.write(message)
            else:
                message = self._colors[record.levelno] + message + self.C_RESET
                stream.write(message)
            stream.write(getattr(self, 'terminator', '\n'))
            self.flush()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.handleError(record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code


class TitledSMTPHandler(_SMTPHandler):
    """可定制邮件主题的 SMTP 日志处理器"""

    def emit(self, record):
        # add extra record
        record.host = __import__("socket").gethostname()
        record.user = __import__("getpass").getuser()
        super(TitledSMTPHandler, self).emit(record)

    def getSubject(self, record):
        formatter = logging.Formatter(fmt=self.subject)
        return formatter.format(record)


class SMTPHandler(logging.Handler):

    def __init__(self, mailhost, fromaddr, toaddrs, subject=None,
                 credentials=None, use_tls=False, use_ssl=False,
                 use_thread=False, charset="utf-8", timeout=10):
        logging.Handler.__init__(self)

        if isinstance(mailhost, (list, tuple)):
            self.mailhost, self.mailport = mailhost
        else:
            self.mailhost, self.mailport = mailhost, None

        if isinstance(credentials, (list, tuple)):
            self.username, self.password = credentials
        else:
            self.username = None

        self.fromaddr = fromaddr
        if isinstance(toaddrs, str):
            toaddrs = [toaddrs]
        self.toaddrs = toaddrs
        self.subject = subject

        self.use_tls = use_tls
        self.use_ssl = use_ssl
        self.use_thread = use_thread
        self.charset = charset
        self.timeout = timeout

    def getSubject(self, record):
        return self.subject

    def _build_mail_attachment(self, attachment):
        att_name = os.path.basename(attachment)
        ctype, encoding = mimetypes.guess_type(attachment)
        if ctype is None or encoding is not None:
            ctype = 'application/octet-stream'
        maintype, subtype = ctype.split('/', 1)

        with open(attachment, 'rb') as fp:
            content = fp.read()
        if maintype == "text":
            att = MIMEText(content, _subtype=subtype, _charset=self.charset)
        elif maintype == "image":
            att = MIMEImage(content, _subtype=subtype)
        elif maintype == "audio":
            att = MIMEAudio(content, _subtype=subtype)
        else:
            att = MIMEBase(maintype, subtype)
            att.set_payload(content)
            email.encoders.encode_base64(att)

        att.add_header('Content-Type', 'application/octet-stream')
        att.add_header('Content-Disposition', 'attachment', filename=att_name)
        return att

    def _set_mail_attachments(self, message, attachments):
        for att in attachments:
            if not isinstance(att, import_module("email.message").Message):
                att = self._build_attachment(att)
            message.attach(att)
        return message

    def _build_mail_message(self, record):
        msg = MIMEMultipart()
        msg['From'] = self.fromaddr
        msg['To'] = ','.join(self.toaddrs)
        msg['Subject'] = self.getSubject(record)
        msg['Date'] = time.strftime("%Y-%m-%d %H:%M:%S %a", time.localtime())

        _msg = self.format(record)
        content = MIMEText(_msg, _subtype="plain", _charset=self.charset)
        msg.attach(content)

        attachments = getattr(record, "attachments", None)
        if attachments:
            self._set_mail_attachments(msg, attachments)

        return msg

    def _sendmail(self, record):
        try:
            if self.use_ssl:
                SMTP_SSL = import_module("smtplib").SMTP_SSL
                server = SMTP_SSL(self.mailhost, self.mailport, timeout=self.timeout)
            else:
                SMTP = import_module("smtplib").SMTP
                server = SMTP(self.mailhost, self.mailport, timeout=self.timeout)

            mailmsg = self._build_mail_message(record)

            if self.username:
                if self.use_tls is not None:
                    smtp.ehlo()
                    smtp.starttls(*self.secure)
                    smtp.ehlo()
                smtp.login(self.username, self.password)

            smtp.sendmail(self.fromaddr, self.toaddrs, mailmsg.as_string())
            smtp.quit()
        except Exception:
            self.handleError(record)

    def emit(self, record):
        if self.use_thread:
            Thread = import_module("threading").Thread
            thread = Thread(target=self._sendmail, args=(record,))
            thread.start()
        else:
            self._sendmail(record)


def setup_logging(reset=False, enable_smtp_log=False):
    """初始化根日志器"""
    logger = logging.getLogger()

    if len(logger.handlers) > 0 and not reset:
        logger.debug("logging has been set up")
        return

    logger.handlers = []  # 重置处理器列表
    logger.setLevel(logging.DEBUG)

    formatter = SystemLogFormatter(DEFAULT_LOG_FORMAT,
                                   datefmt='%Y-%m-%d %H:%M:%S,%f')

    stream_handler = ColoredStreamHandler(sys.stdout)
    stream_handler.setLevel(logging.DEBUG)
    stream_handler.setFormatter(formatter)
    logger.addHandler(stream_handler)

    # 添加邮件日志处理器，将错误日志通过邮件发送出去
    email_toaddrs = os.getenv("EMAIL_TOADDRS")
    if enable_smtp_log and email_toaddrs:
        email_host = os.getenv("EMAIL_HOST")
        email_address = os.getenv("EMAIL_ADDRESS")
        email_username = os.getenv("EMAIL_USERNAME")
        email_password = os.getenv("EMAIL_PASSWORD")
        email_toaddrs = os.getenv("EMAIL_TOADDRS")
        email_use_tls = os.getenv("EMAIL_USE_TLS")

        if ":" in email_host:
            email_host = tuple(
                convert(item) for convert, item in
                zip((str, int), email_host.split(":", 1))
            )
        email_toaddrs = [item.strip() for item in email_toaddrs.split(",")]
        email_fromaddr = ("{}<{}>".format(email_username, email_address)
                          if email_username else email_address)
        email_use_tls = bool(email_use_tls)

        params = dict(
            mailhost=email_host,
            fromaddr=email_fromaddr,
            toaddrs=email_toaddrs,
            subject="%(name)s Error: %(message)s",
            credentials=(email_address, email_password)
        )
        smtp_handler = TitledSMTPHandler(**params)
        smtp_handler.setLevel(logging.ERROR)
        smtp_handler.setFormatter(SystemLogFormatter(DEFAULT_ERROR_MAIL_FORMAT))
        logger.addHandler(smtp_handler)

    return logger
